{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "d1822a7d-5698-4f4d-92ec-7f712fac9d49",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import re\n",
    "import jieba\n",
    "from tqdm import tqdm\n",
    "import torch\n",
    "from opencc import OpenCC"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "6831f6f9-c45e-4e48-82e9-e8f9b02d207f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "device(type='cuda')"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 检查是否有可用的 GPU\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "device"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "1816afb0-2565-4fbd-917d-54c4b391fc9e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "数据数量508110\n",
      "                                                  content  stars  label\n",
      "23638                                         虽然不长 但是真的不错      4      1\n",
      "477707  本该是三个骗子互相拆台的一出好戏，变成了两个傻子在一个骗子的疯狂摆布下，不得不演上一出“老婆...      2      0\n",
      "497163      7.0分\\n补标\\n 个人并不怎么喜欢这个电影，催泪是催泪，但是过度的美化让我觉得很虚假。      3      0\n",
      "312039  本質上與《密陽》是相似的，只有最虔誠的教徒，才能拍出深刻懷疑宗教本質的作品。當對某種事物信仰...      3      0\n",
      "262080  喜欢结尾部分，两全其美，孩子快乐，公司也能照常运行，而且大猫咪和小女孩重新见面了。\\n大眼仔...      3      0\n"
     ]
    }
   ],
   "source": [
    "# 读取数据集\n",
    "data_path = '../datasets/chinese_movie_reviews/chinese_movie_reviews_datasets.jsonl'\n",
    "df = pd.read_json(data_path, orient='records', lines=True)\n",
    "print(f'数据数量{len(df)}')\n",
    "print(df.sample(5))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "97bd708c-5e3a-487b-a8b9-026f91c9b685",
   "metadata": {},
   "source": [
    "## 数据预处理\n",
    "\n",
    "1. 分词\n",
    "2. 训练Word2Vec：生成一个包含语料库中的每个词的向量空间"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "6da13e00-6a47-453a-8faf-fd551b5c8ac4",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Building prefix dict from the default dictionary ...\n",
      "Loading model from cache C:\\Users\\PC\\AppData\\Local\\Temp\\jieba.cache\n",
      "Loading model cost 0.531 seconds.\n",
      "Prefix dict has been built successfully.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "最大文本长度：625\n",
      "平均文本长度：21.04\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>content</th>\n",
       "      <th>stars</th>\n",
       "      <th>label</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>249264</th>\n",
       "      <td>亲生女儿 床上 试探 对方 性爱 技术 行不行 老鸨 母亲 男妓 性虐 自己 老婆 还要 旁...</td>\n",
       "      <td>4</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>225658</th>\n",
       "      <td>毕 格罗 真是 美国 主旋律 中 找到 一条 永远 不会错 套路 整体 喜欢 真实感 戏剧化...</td>\n",
       "      <td>4</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>505707</th>\n",
       "      <td>白夜 行 嫌疑人 导演 会 玩</td>\n",
       "      <td>3</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>77225</th>\n",
       "      <td>反正 儿子 喜欢 就行 过年 嘛</td>\n",
       "      <td>4</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>313291</th>\n",
       "      <td>大家 普及 下万 台币 约等于 万 人民币</td>\n",
       "      <td>3</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                                  content  stars  label\n",
       "249264  亲生女儿 床上 试探 对方 性爱 技术 行不行 老鸨 母亲 男妓 性虐 自己 老婆 还要 旁...      4      1\n",
       "225658  毕 格罗 真是 美国 主旋律 中 找到 一条 永远 不会错 套路 整体 喜欢 真实感 戏剧化...      4      1\n",
       "505707                                    白夜 行 嫌疑人 导演 会 玩      3      0\n",
       "77225                                    反正 儿子 喜欢 就行 过年 嘛      4      1\n",
       "313291                              大家 普及 下万 台币 约等于 万 人民币      3      0"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 初始化 OpenCC 转换器（繁转简）\n",
    "cc = OpenCC('t2s')  # t2s 表示繁体转简体\n",
    "\n",
    "# 读取停用词表\n",
    "with open('../datasets/chinese_movie_reviews/stopwords.txt', 'r', encoding='utf-8') as f:\n",
    "    stopwords = [line.strip() for line in f.readlines()]\n",
    "\n",
    "# 繁体字转简体字\n",
    "def traditional_to_simplified(text):\n",
    "    return cc.convert(text)\n",
    "\n",
    "# 移除数字\n",
    "def remove_digit(text):\n",
    "     return re.sub(r'\\d+', '', text)\n",
    "\n",
    "# 分词处理\n",
    "def tokenize(text):\n",
    "    return \" \".join(jieba.cut(text))\n",
    "\n",
    "def remove_stopwords(text):\n",
    "    return \" \".join([word for word in text.split() if word not in stopwords])\n",
    "\n",
    "# 应用预处理\n",
    "def process_row(text):\n",
    "    text = remove_digit(text) # 移除数字\n",
    "    text = traditional_to_simplified(text)  # 繁体字转简体字\n",
    "    text = tokenize(text)\n",
    "    text = remove_stopwords(text)\n",
    "    return text\n",
    "\n",
    "df[\"content\"] = df[\"content\"].apply(process_row) # 作者在这里大概用了2分钟\n",
    "\n",
    "# 计算每条文本的长度\n",
    "sentence_lengths = df[\"content\"].apply(lambda x: len(x.split()))  # 计算每条文本的词数（已经分词）\n",
    "# 计算最大长度和平均长度\n",
    "max_length = sentence_lengths.max()\n",
    "avg_length = sentence_lengths.mean()\n",
    "print(f\"最大文本长度：{max_length}\")\n",
    "print(f\"平均文本长度：{avg_length:.2f}\")\n",
    "\n",
    "df.sample(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "90c0beb8-daab-4f56-bd8e-ca6391ad74d0",
   "metadata": {},
   "source": [
    "#### Word2Vec\n",
    "\n",
    "`Word2Vec训练很快，一分钟以内就能结束`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "4c737eff-6d74-4c26-9acb-fcdd69cb0364",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from gensim.models import Word2Vec\n",
    "\n",
    "def prepare_data(df, vector_size, max_length=100):\n",
    "    # 将文本转换为词列表\n",
    "    texts = df['content'].apply(lambda x: x.split())\n",
    "\n",
    "    # 首先创建一个空的词汇表并添加 <PAD> 词索引为 0\n",
    "    vocab = {\"<PAD>\": 0}\n",
    "    \n",
    "    # 训练Word2Vec模型\n",
    "    w2v_model = Word2Vec(sentences=texts, vector_size=vector_size, window=8, min_count=1, workers=4)\n",
    "    \n",
    "    # 获取模型训练后生成的词汇表\n",
    "    vocab.update({k: v+1 for k, v in w2v_model.wv.key_to_index.items()})\n",
    "    \n",
    "    # 将文本转换为序列，如果词不在词汇表中，则用0表示\n",
    "    sequences = [[vocab.get(word, 0) for word in text] for text in texts]\n",
    "\n",
    "    # 对每个序列进行填充或截断\n",
    "    padded = [s[:max_length] + [0] * (max_length - len(s)) if len(s) < max_length \n",
    "              else s[:max_length] for s in sequences]\n",
    "    \n",
    "    embedding_matrix = np.zeros((len(vocab), vector_size))\n",
    "    for word, i in vocab.items():\n",
    "        if word != \"<PAD>\":  # 确保 <PAD> 不会被赋予任何词向量\n",
    "            embedding_matrix[i] = w2v_model.wv[word]\n",
    "    \n",
    "    return np.array(padded), embedding_matrix, vocab\n",
    "\n",
    "vector_size=64\n",
    "padded, embedding_matrix, vocab = prepare_data(df, vector_size=vector_size)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "872a2c11-6a54-4485-89c9-006af96d0524",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "词汇表大小： 276419\n",
      "词汇表的一部分:\n",
      "词: <PAD>, 索引: 0\n",
      "词: 看, 索引: 1\n",
      "词: 人, 索引: 2\n",
      "词: 但, 索引: 3\n",
      "词: 好, 索引: 4\n",
      "狗 的词向量:\n",
      "[ 3.16218114 -6.14741898  0.45720214  1.25703156  0.1533884   1.27312267\n",
      " -0.27875459  1.01880586  2.33547425  1.80326021  3.15771031 -2.79145074\n",
      "  2.76341057 -2.16252637  0.60111237 -1.56099725 -1.9911598   1.41523695\n",
      " -0.49740243 -0.77958089 -3.30216146 -0.86797374  1.85962439 -3.29307961\n",
      " -0.22757535  0.89264297 -0.23079529  3.71837664  2.57455182  4.17028141\n",
      "  3.17933297  1.90085232]\n"
     ]
    }
   ],
   "source": [
    "# 查看词汇表的前几个词\n",
    "print(f'词汇表大小： {len(vocab)}')\n",
    "print(\"词汇表的一部分:\")\n",
    "for word, idx in list(vocab.items())[:5]:  # 查看前5个词\n",
    "    print(f\"词: {word}, 索引: {idx}\")\n",
    "\n",
    "# 查看嵌入矩阵中对应某个词的词向量\n",
    "word_to_check = '狗'  \n",
    "if word_to_check in vocab:\n",
    "    word_index = vocab[word_to_check]\n",
    "    word_vector = embedding_matrix[word_index]\n",
    "    print(f\"{word_to_check} 的词向量:\")\n",
    "    print(word_vector)\n",
    "else:\n",
    "    print(f\"词汇表中没有 {word_to_check} 这个词。\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a29ae4b5-f74a-4aae-a3fa-b3b6fcee0fe2",
   "metadata": {},
   "source": [
    "#### 构建数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "d798fe2c-4a7b-40c6-853e-659d2fcc8106",
   "metadata": {},
   "outputs": [],
   "source": [
    "from torch.utils.data import Dataset, DataLoader\n",
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "class TextDataset(Dataset):\n",
    "    def __init__(self, sequences, labels):\n",
    "        self.sequences = torch.LongTensor(sequences)\n",
    "        self.labels = torch.LongTensor(labels)\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self.labels)\n",
    "    \n",
    "    def __getitem__(self, idx):\n",
    "        return self.sequences[idx], self.labels[idx]\n",
    "\n",
    "X = padded\n",
    "y = df['label'].values\n",
    "# stratify=df[\"label\"] 使得训练集和测试集中的标签分布是均匀\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=42, stratify=df[\"label\"])  \n",
    "\n",
    "train_dataset = TextDataset(X_train, y_train)\n",
    "test_dataset = TextDataset(X_test, y_test)\n",
    "\n",
    "train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)\n",
    "test_loader = DataLoader(test_dataset, batch_size=32)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "71f5b357-af62-4af1-b467-f71fd1c5ed25",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "训练集大小: 457299\n",
      "测试集大小: 50811\n",
      "训练集标签分布: Counter({1: 228650, 0: 228649})\n",
      "测试集标签分布: Counter({0: 25406, 1: 25405})\n"
     ]
    }
   ],
   "source": [
    "# 查看训练集和测试集的大小\n",
    "print(f\"训练集大小: {len(train_dataset)}\")\n",
    "print(f\"测试集大小: {len(test_dataset)}\")\n",
    "\n",
    "# 查看训练集和测试集的标签分布\n",
    "from collections import Counter\n",
    "\n",
    "train_labels_counter = Counter(y_train)\n",
    "test_labels_counter = Counter(y_test)\n",
    "print(f\"训练集标签分布: {train_labels_counter}\")\n",
    "print(f\"测试集标签分布: {test_labels_counter}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0d58254-08f2-43fc-a87e-23b4943cfafb",
   "metadata": {},
   "source": [
    "## 定义模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "8559dadf-c087-4650-a68c-cee6efcfeef5",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "class TextClassifier(nn.Module):\n",
    "    def __init__(self, vocab_size, embedding_dim, embedding_matrix, hidden_dim):\n",
    "        super().__init__()\n",
    "        # 定义词嵌入层，使用 embedding_matrix 初始化\n",
    "        self.embedding = nn.Embedding.from_pretrained(\n",
    "            torch.FloatTensor(embedding_matrix),\n",
    "            padding_idx=0\n",
    "        )\n",
    "        self.embedding.weight.requires_grad = False # True 确保嵌入层的参数可训练\n",
    "        \"\"\"\n",
    "        双向 LSTM 层：输入维度为 embedding_dim，输出维度为 hidden_dim。\n",
    "        batch_first=True : 输入张量的形状为 (batch_size, sequence_length)。\n",
    "        bidirectional=True : LSTM 会在两个方向上（正向和反向）处理输入序列，以捕捉更多上下文信息\n",
    "        (因为 LSTM 是双向的，它的输出将是两个隐藏层的连接, 所以实际输出维度为 hidden_dim * 2)\n",
    "        \"\"\"\n",
    "        self.lstm = nn.LSTM(\n",
    "            embedding_dim,    # 输入特征的维度\n",
    "            hidden_dim,       # 隐藏状态的维度\n",
    "            num_layers=2,     # LSTM的层数\n",
    "            batch_first=True, # 输入和输出的张量的第一个维度是batch_size\n",
    "            bidirectional=True, # 使用双向LSTM\n",
    "            dropout=0.5\n",
    "        )\n",
    "        self.fc1 = nn.Linear(hidden_dim * 2, hidden_dim)  # 添加一个额外的全连接层\n",
    "        self.fc2 = nn.Linear(hidden_dim, 2)  # 二分类任务\n",
    "        self.dropout = nn.Dropout(0.5)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        embedded = self.embedding(x)\n",
    "        lstm_out, _ = self.lstm(embedded) # 第二个返回值_是LSTM的隐藏状态和单元状态；lstm_out形状： (batch_size, sequence_length, hidden_dim * 2)\n",
    "        last_hidden = lstm_out[:, -1, :] # 选择每个批次中的最后一个时刻的输出，形状为 (batch_size, hidden_dim * 2)\n",
    "        dropped = self.dropout(last_hidden)\n",
    "        fc1_out = F.relu(self.fc1(dropped))\n",
    "        fc_out = self.fc2(fc1_out)\n",
    "        return fc_out\n",
    "\n",
    "hidden_dim=64\n",
    "model = TextClassifier(len(vocab) + 1, vector_size, embedding_matrix, hidden_dim).to(device)\n",
    "loss_fn = nn.CrossEntropyLoss()\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)\n",
    "\n",
    "# 初始化学习率调度器，每step_size个epoch将学习率衰减为原来的gamma倍\n",
    "scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=2, gamma=0.5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "c7c7826e-5f40-474f-a02a-1d412d87c6ff",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TextClassifier(\n",
      "  (embedding): Embedding(276419, 32, padding_idx=0)\n",
      "  (lstm): LSTM(32, 64, num_layers=2, batch_first=True, dropout=0.5, bidirectional=True)\n",
      "  (fc1): Linear(in_features=128, out_features=64, bias=True)\n",
      "  (fc2): Linear(in_features=64, out_features=2, bias=True)\n",
      "  (dropout): Dropout(p=0.5, inplace=False)\n",
      ")\n",
      "模型总参数数量: 9,003,298\n",
      "模型可训练参数数量: 157,890\n"
     ]
    }
   ],
   "source": [
    "# 查看模型结构\n",
    "# 打印模型参数总数和可训练参数总数\n",
    "def count_parameters(model):\n",
    "    total_params = sum(p.numel() for p in model.parameters())  # 所有参数数量\n",
    "    trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)  # 需要训练的参数数量\n",
    "    print(f\"模型总参数数量: {total_params:,}\")\n",
    "    print(f\"模型可训练参数数量: {trainable_params:,}\")\n",
    "\n",
    "print(model)\n",
    "count_parameters(model)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "66a0e293-831e-4ace-8ab3-981be5be48f8",
   "metadata": {},
   "source": [
    "## 模型训练与评估"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "fdbfde3d-bcc5-4bbc-b954-d3e576d1cb90",
   "metadata": {},
   "outputs": [],
   "source": [
    "from tqdm import tqdm\n",
    "\n",
    "# 训练函数\n",
    "def train(dataloader, model, loss_fn, optimizer):\n",
    "    model.train()  # 设置模型为训练模式\n",
    "    \n",
    "    running_loss = 0.0\n",
    "    correct = 0\n",
    "    total = 0\n",
    "\n",
    "    # 使用 tqdm 包裹数据加载器，显示进度条\n",
    "    progress_bar = tqdm(dataloader, desc=\"Training\", leave=False)\n",
    "    for texts, labels in progress_bar:\n",
    "        # 将数据移动到设备\n",
    "        texts, labels = texts.to(device), labels.to(device)\n",
    "\n",
    "        # 前向传播\n",
    "        outputs = model(texts)\n",
    "        loss = loss_fn(outputs, labels)\n",
    "\n",
    "        # 反向传播和优化\n",
    "        optimizer.zero_grad()\n",
    "        loss.backward()\n",
    "        optimizer.step()  # 更新模型参数\n",
    "\n",
    "        # 统计指标\n",
    "        running_loss += loss.item()\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        total += labels.size(0)\n",
    "        correct += (predicted == labels).sum().item()\n",
    "\n",
    "        # 更新进度条描述\n",
    "        progress_bar.set_postfix(lr=optimizer.param_groups[0]['lr'], loss=loss.item())\n",
    "        \n",
    "    scheduler.step()  # 更新学习率       \n",
    "\n",
    "    accuracy = 100 * correct / total\n",
    "    avg_loss = running_loss / len(dataloader)\n",
    "    return avg_loss, accuracy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "36267fd1-6b5e-4a5e-b9a7-e97652a9948c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 测试函数\n",
    "def evaluate(dataloader, model, loss_fn):\n",
    "    model.eval()  # 设置模型为评估模式\n",
    "    running_loss = 0.0\n",
    "    correct = 0\n",
    "    total = 0\n",
    "\n",
    "    with torch.no_grad():  # 关闭梯度计算\n",
    "        progress_bar = tqdm(dataloader, desc=\"Evaluating\", leave=False)\n",
    "        for texts, labels in progress_bar:\n",
    "            # 将数据移动到设备\n",
    "            texts, labels = texts.to(device), labels.to(device)\n",
    "\n",
    "            # 前向传播\n",
    "            outputs = model(texts)\n",
    "            loss = loss_fn(outputs, labels)\n",
    "\n",
    "            # 统计指标\n",
    "            running_loss += loss.item()\n",
    "            _, predicted = torch.max(outputs, 1)\n",
    "            total += labels.size(0)\n",
    "            correct += (predicted == labels).sum().item()\n",
    "            # 更新进度条描述\n",
    "            progress_bar.set_postfix(loss=loss.item())\n",
    "\n",
    "    accuracy = 100 * correct / total\n",
    "    avg_loss = running_loss / len(dataloader)\n",
    "    return avg_loss, accuracy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "59d90b49-560f-40b2-a1cd-d2fe798c8910",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/10\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                                                       \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 1, Train_acc:57.1%, Train_loss:0.675, Test_acc:64.4%，Test_loss:0.631\n",
      "Epoch 2/10\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                                                       \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 2, Train_acc:65.7%, Train_loss:0.613, Test_acc:67.5%，Test_loss:0.592\n",
      "Epoch 3/10\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                                                       \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 3, Train_acc:67.5%, Train_loss:0.591, Test_acc:68.0%，Test_loss:0.585\n",
      "Epoch 4/10\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                                                       \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 4, Train_acc:68.1%, Train_loss:0.584, Test_acc:67.9%，Test_loss:0.582\n",
      "Epoch 5/10\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                                                       \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 5, Train_acc:68.6%, Train_loss:0.576, Test_acc:68.2%，Test_loss:0.582\n",
      "Epoch 6/10\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                                                       \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 6, Train_acc:68.9%, Train_loss:0.572, Test_acc:68.4%，Test_loss:0.578\n",
      "Epoch 7/10\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                                                       \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 7, Train_acc:69.3%, Train_loss:0.567, Test_acc:68.2%，Test_loss:0.578\n",
      "Epoch 8/10\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                                                       \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 8, Train_acc:69.5%, Train_loss:0.565, Test_acc:68.3%，Test_loss:0.579\n",
      "Epoch 9/10\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                                                       \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 9, Train_acc:69.7%, Train_loss:0.562, Test_acc:68.2%，Test_loss:0.579\n",
      "Epoch 10/10\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                                                       "
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:10, Train_acc:69.8%, Train_loss:0.561, Test_acc:68.4%，Test_loss:0.579\n",
      "训练完成!\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\r"
     ]
    }
   ],
   "source": [
    "# 开始训练\n",
    "\n",
    "num_epochs = 10\n",
    "\n",
    "train_loss = []\n",
    "train_acc  = []\n",
    "test_loss  = []\n",
    "test_acc   = []\n",
    "\n",
    "for epoch in range(num_epochs):\n",
    "    print(f\"Epoch {epoch+1}/{num_epochs}\")\n",
    "    \n",
    "    epoch_train_loss, epoch_train_acc = train(train_loader, model, loss_fn, optimizer)\n",
    "\n",
    "    # 在测试集上评估\n",
    "    epoch_test_loss, epoch_test_acc = evaluate(test_loader, model, loss_fn)\n",
    "\n",
    "    train_acc.append(epoch_train_acc)\n",
    "    train_loss.append(epoch_train_loss)\n",
    "    test_acc.append(epoch_test_acc)\n",
    "    test_loss.append(epoch_test_loss)\n",
    "    # 打印训练和测试结果\n",
    "    template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%，Test_loss:{:.3f}')\n",
    "    print(template.format(epoch+1, epoch_train_acc, epoch_train_loss, epoch_test_acc, epoch_test_loss))\n",
    "\n",
    "print(\"训练完成!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dcb37d44-4e75-4a91-9041-2ffe8f030b7e",
   "metadata": {},
   "source": [
    "## 结果可视化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "21e1b89f-7b7f-4903-bddb-500fe3f39514",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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SYiK5L333mPp0qVcu3atwEFXhBCispBrByFESZDu3OKxkNtlNigoiKysLPbs2cOnn35K37595UtQiEIcOXKE8+fPs2TJEt544w1JoIUQlYpcOwghSosk0eKxYG1tzaJFi7hy5QoZGRlUrVqV8ePHM2XKFGOHJkS51bx5c6ytrenWrRszZ840djhCCFGm5NpBCFFapDu3EEIIIYQQQghRRDLFlRBCCCGEEEIIUUSSRAshhBAV3JIlSwgICMDS0pLGjRtz8ODB+26fkZHB5MmT8fPzw8LCgsDAQP0ULbkWL15MrVq1sLKy0k81k56eXpqnIYQQQpQLck+0EEIIUYGtX7+eUaNGsWTJEkJCQvjyyy/p0qULZ86coWrVqgXu07NnT27evMny5cupXr06MTExBnPjrlmzhgkTJrBixQpatGjB+fPnGThwIACLFi0qi9MSQgghjKbc3ROt1Wq5ceMGdnZ2qFQqY4cjhBBCoCgKycnJeHl5YWLyeHXiatasGY0aNWLp0qX6suDgYF544QXmzJmTb/vt27fz6quvEh4eTpUqVQo85ttvv83Zs2fZvXu3vuy9997j2LFjD2zlziXf90IIIcqT4nzXl7uW6Bs3buDr62vsMIQQQoh8rl279lhNjZOZmcmJEyeYMGGCQXnHjh05dOhQgfts2bKFJk2aMG/ePL777jtsbGzo3r07M2bMwMrKCoCWLVuyevVqjh07RtOmTQkPD2fr1q0MGDCg0FgyMjLIyMjQP4+MjKR27dolcJZCCCFEySnKd325S6Lt7OwAXfD29vZGjkYIIYSApKQkfH199d9Rj4vY2Fg0Gg3u7u4G5e7u7kRHRxe4T3h4OL///juWlpZs3ryZ2NhYRowYQVxcnP6+6FdffZVbt27RsmVLFEUhOzubN998M1+yntecOXOYPn16vnL5vhdCCFEeFOe7vtwl0blduuzt7eVLVQghRLnyuHY7vjduRVEKPRetVotKpWLNmjU4ODgAsHDhQnr06MHnn3+OlZUV+/btY9asWSxZsoRmzZpx8eJF3n33XTw9Pfnggw8KPO7EiRMZM2aM/nnuxYp83wshhChPivJdX6wbu/z9/VGpVPmWt956C9B9KU+bNg0vLy+srKxo06YN//3338NFL4QQQohH4uLiglqtztfqHBMTk691Openpyfe3t76BBp091ArisL169cB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43v33XeRlZWF3NxcdOzYUehwWrQzZ86gpKQEkZGRlm1GoxG5ubnYtGkTdDodJBKJgBG2PGq1Gt27d7fa1q1bN+zdu1egiJzDe++9h/nz52PMmDEAgLCwMNy4cQOpqalMrCQI5nrHYq63H+Z6x2C+tz/mets55T3RcrkckZGRyM7OttqenZ2Nfv36CRRVy2c2mzFjxgzs27cPR44cQXBwsNAhtXiDBw9Gfn4+8vLyLI+oqCiMHTsWeXl5TKpPICYmptFyLFeuXEFQUJBAETmHmpoaiMXWKUMikXDZCxIMc71jMNfbH3O9YzDf2x9zve2csicaAJKSkvDWW28hKioK0dHRSE9PR0FBAaZOnSp0aC1WQkICdu3ahS+++AJubm6WX/89PDygUqkEjq5lcnNza3SfmaurK7y9vXn/2ROaPXs2+vXrh5SUFPzud7/D119/jfT0dKSnpwsdWos2cuRIrFy5EoGBgQgNDcW5c+ewbt06TJw4UejQqBVjrrc/5nr7Y653DOZ7+2OufwxmJ/bhhx+ag4KCzHK53BwREWHOyckROqQWDcADH9u3bxc6NKcyaNAg86xZs4QOo0Xbv3+/uUePHmaFQmHu2rWrOT09XeiQWjyNRmOeNWuWOTAw0KxUKs0hISHmhQsXmnU6ndChUSvHXG9fzPXPBnO9fTDf2xdzve2cdp1oIiIiIiIiIntzynuiiYiIiIiIiByBRTQRERERERGRjVhEExEREREREdmIRTQRERERERGRjVhEExEREREREdmIRTQRERERERGRjVhEExEREREREdmIRTQRERERERGRjVhEExEREREREdmIRTQRERERERGRjVhEExEREREREdmIRTQRERERERGRjf4fCJDsXGmCpi4AAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 1200x300 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "epochs_range = range(num_epochs)\n",
    "\n",
    "plt.figure(figsize=(12, 3))\n",
    "plt.subplot(1, 2, 1)\n",
    "\n",
    "plt.plot(epochs_range, train_acc, label='Training Accuracy')\n",
    "plt.plot(epochs_range, test_acc, label='Test Accuracy')\n",
    "plt.legend(loc='lower right')\n",
    "plt.title('Training and Validation Accuracy')\n",
    "\n",
    "plt.subplot(1, 2, 2)\n",
    "plt.plot(epochs_range, train_loss, label='Training Loss')\n",
    "plt.plot(epochs_range, test_loss, label='Test Loss')\n",
    "plt.legend(loc='upper right')\n",
    "plt.title('Training and Validation Loss')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd2fedec-730a-46ac-bdd4-742ee0a9dc43",
   "metadata": {},
   "source": [
    "### 模型保存"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "80d81cd6-9e6d-435e-a6ff-5dbd32590c47",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 指定保存路径\n",
    "save_dir = '../models/3_Chinese_Movie_review_Text_Classification'\n",
    "\n",
    "# 确保目录存在，如果不存在则创建\n",
    "import os\n",
    "if not os.path.exists(save_dir):\n",
    "    os.makedirs(save_dir)\n",
    "\n",
    "\n",
    "# 保存模型参数和权重\n",
    "checkpoint = {\n",
    "    'vocab' : vocab,\n",
    "    'vocab_size': len(vocab) + 1,\n",
    "    'embedding_dim': vector_size,\n",
    "    'embedding_matrix': embedding_matrix,\n",
    "    'hidden_dim': hidden_dim,\n",
    "    'model_state_dict': model.state_dict()\n",
    "}\n",
    "# 保存到文件\n",
    "torch.save(checkpoint, '../models/3_Chinese_Movie_review_Text_Classification/model_checkpoint.pth')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f779e7ac-36ce-444b-af9c-6eb63264b99f",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.20"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
